/**
 * 测试用例excel表格导出
 *
 * Created by nongzhizhong on 2021/12/07
 */
const fs = require('fs-extra');
const path = require('path');
const compressing = require('compressing');
const XlsxTemplate = require('xlsx-template');
const { TestCaseTransformer } = require('smart-rocket-unit');
const BaseTestCasesExporter = require('./../base-exporter');
const common = require('./../../../common');
const testModel = require('../../../../models/tests');
const classCreator = require('../../report/base-report-test-case-transformer');
const testCaseGroups = require('../../../../models/test-case-groups');
const utility = require('../../../utility');
class ExcelTestCasesExporter extends BaseTestCasesExporter {
  exportTestCases(progressIndicator) {
    const result = {
      cachePath: this.outputDir,
    };
    return new Promise((resolve, reject) => {
      this.spliteByFunction(null, progressIndicator)
        .then(() => {
          const fileName = `${this.project.projectName}-${this.version.versionName}.test-cases.xlsx.zip`;
          result.testsPath = path.join(this.outputDir, fileName);
          const testsDir = path.join(this.outputDir, this.project.projectName);
          return compressing.zip.compressDir(testsDir, result.testsPath);
        })
        .then(() => {
          resolve(result);
        })
        .catch(reject);
    });
  }

  /**
   * 按文件分割
   * @return {Promise}
   */
  spliteByFile(callback = null, progressIndicator) {
    return new Promise((resolve, reject) => {
      const result = {};
      this.spliteByProjectVersion((versionDir) => fs.ensureDir(versionDir))
        .then(({ projectDir, versionPath, versionType }) => {
          result.projectDir = projectDir;
          result.versionPath = versionPath;
          result.versionDir = versionPath;
          result.versionType = versionType;
          return this.filterSelectedFiles();
        })
        .then(() => this.createVersionInfoFile(result.projectDir, result.versionType, 'xlsx'))
        .then(() => this.handleFiles(this.fileIds, result.versionDir, callback, progressIndicator))
        .then(() => resolve(result))
        .catch(reject);
    });
  }

  /**
 * 按函数拆分测试用例
 * @param {object} func 函数信息
 * @param {object} file 文件信息
 * @param {object} typeSystem 类型系统
 * @param {string} versionDir 版本路径
 * @returns
 */
  handleFunction(func, file, typeSystem, versionDir) {
    if (!file.functions || (file.functions.length > 0
      && !file.functions.includes(func._id.toString()))) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) => {
      const { mangledId, functionName } = func;
      const testsPath = `${path.join(versionDir, file.path, `${functionName}-${mangledId}`)}.xlsx`;
      super.handleFunction(func, file, typeSystem, versionDir)
        .then(() => testCaseGroups.getGroupByFunctionId(func._id))
        .then((groups) => this.exportTestCasesByFunction(
          testsPath,
          typeSystem,
          {
            tests: func.tests,
            functionName: func.functionName,
            mangledId,
            variables: func.variables,
            groups,
          }
        ))
        .then(resolve)
        .catch(reject);
    });
  }

  getTestCaseSummaryJson(testCase) {
    const result = this.buildTestCaseResult(testCase);
    const json = {
      ...testCase,
      requirementInfo: testCase.requirementInfo || '',
      resultEvaluation: result.message.kind,
    };
    delete json.result;
    delete json.log;
    return json;
  }

  /**
   *格式化测试用例数据
   * @param {array} testCaseDatas 需要输出的数据
   * @returns
   */
  formatTestCaseData(testCaseDatas) {
    let expectedResults = '';
    let actualedResults = '';
    let tolerances = '';
    let inputStr = '';
    let categoryState = false;
    testCaseDatas.forEach((item) => {
      if (item.category && item.category !== '') {
        let enter = '\n';
        if (categoryState) {
          enter = '\n\n';
        }
        expectedResults += `${enter + item.category}`;
        actualedResults += `${enter + item.category}`;
        tolerances += `${enter + item.category}`;
        inputStr += `${enter + item.category}`;
        categoryState = false;
      }
      if (item.tolerance) {
        tolerances += `\n  准则:[+${item.tolerance['+']},-${item.tolerance['-']}];`;
        categoryState = true;
      }
      if (item.variable && item.variable !== '') {
        expectedResults += `\n  ${item.variable}`;
        actualedResults += `\n  ${item.variable}`;
        tolerances += `\n  ${item.variable}`;
        inputStr += `\n  ${item.variable}`;
        categoryState = true;
      }
      if (item.variable && item.value === '') {
        expectedResults += ';';
        tolerances += ';';
        inputStr += ';';
      }
      if (item.value && item.value !== '') {
        expectedResults += `=${item.value};`;
        tolerances += `=${item.value};`;
        inputStr += `=${item.value};`;
        categoryState = true;
      }
      if (item.result && item.result !== '') {
        actualedResults += `=${item.result};`;
        categoryState = true;
      }
      if (item.variable && item.result === '') {
        actualedResults += ';';
      }

      if (item.location) {
        const begin = `起始:(${item.location.begin.line},${item.location.begin.column});`;
        const end = `结束:(${item.location.end.line},${item.location.end.column})`;
        expectedResults += `\n  ${begin + end}`;
        actualedResults += `\n  ${begin + end}`;
        tolerances += `\n  ${begin + end}`;
        inputStr += `\n  ${begin + end}`;
        categoryState = true;
      }
    });
    return {
      expectedResults, actualedResults, tolerances, inputStr,
    };
  }

  /**
   * 导出单个函数的测试用例
   * @param {String} testsPath 测试用例路径
   * @param {Object} typeSystem 类型系统
   * @param {Array} functions 函数信息列表
   * @returns {Promise}
   */
  exportTestCasesByFunction(testsPath, typeSystem, func) {
    return new Promise((resolve, reject) => {
      const { versionType } = this.version;
      common.complementByProjectVersionId(this.versionId, this.version, this.project, this.creator)
        .then(() => fs.ensureDir(path.dirname(testsPath)))
        .then(() => fs.readFile(path.join(__dirname, 'templates', 'file-template.xlsx')))
        .then((templateData) => {
          const template = new XlsxTemplate(templateData);
          const sheetNumber = 1;
          const exportData = {
            testCases: [],
          };
          const { tests, mangledId } = func;
          const removeTestCaseFields = [
            'projectId',
            'versionId',
            'fileId',
            'functionId',
            'log',
            '__v',
            'description',
            'requirementInfo',
            'taskId',
            'statementCovered',
            'branchCovered',
            'conditionCovered',
            'decisionCovered',
            'mcdcCovered',
            'invokedFuncCovered',
            'invokeCovered',
            'resultKind',
            'status',
          ];
          func.groups = func.groups.map((group) => {
            group = group.toObject();
            Object.keys(group).forEach((key) => {
              if (removeTestCaseFields.includes(key) && key !== 'description') {
                delete group[key];
              }
            });
            return group;
          });
          const newVariables = JSON.parse(JSON.stringify(func.variables));
          Object.keys(newVariables).forEach((key) => {
            if (['_id', '__v', 'fileId', 'versionId', 'projectId', 'functionId'].includes(key)) {
              delete newVariables[key];
            }
          });
          tests.forEach((test) => {
            const newTestCase = JSON.parse(JSON.stringify(test));
            newTestCase.data = newTestCase.data || {};
            Object.keys(newTestCase).forEach((key) => {
              if (removeTestCaseFields.includes(key)) {
                delete newTestCase[key];
              }
            });
            test = testModel.getJson(test, this.i18n);
            const reporTest = JSON.parse(JSON.stringify(test));
            // 导出测试用例报告一致，用来获取输入输出数据
            const ReportTestCaseTransformer = classCreator();
            const reporTransformer = new ReportTestCaseTransformer(
              reporTest,
              func.variables,
              typeSystem,
              this.i18n
            );
            reporTransformer.transform();
            // 测试用例原始的json数据导入使用
            const testTransformer = new TestCaseTransformer(test, func.variables, typeSystem);
            testTransformer.transform();
            test.versionType = versionType;
            test = this.getTestCaseSummaryJson(test);
            test.info = JSON.stringify({
              [mangledId]: {
                functionVariables: newVariables,
                testCases: [newTestCase],
                testCaseGroups: func.groups,
              },
            });
            test.input = (this.formatTestCaseData(reporTransformer.input)).inputStr;
            const outPutData = this.formatTestCaseData(reporTransformer.output);
            test.tolerances = outPutData.tolerances;
            test.expectedResults = outPutData.expectedResults;
            test.actualedResults = outPutData.actualedResults;
            exportData.testCases.push(test);
          });
          template.substitute(sheetNumber, exportData);
          return template.generate({ type: 'uint8array' });
        })
        .then((xlsxData) => fs.writeFile(utility.handleWindwowFileNameSpecialCharacters(testsPath), xlsxData))
        .then(resolve)
        .catch(reject);
    });
  }
}

module.exports = ExcelTestCasesExporter;
